#include <iostream>
#include "ConcreteReactor.h"
#include "INET_Addr.h"
#include "SOCK_Connector.h"
#include "LogEvent.h"
#include "AlarmEvent.h"
#include "PatientValueEvent.h"
#include "RefinedReactor.h"

using namespace std;

#define _portLog 3490
#define _portAlarm 3491
#define _portPatient 3492
#define _localAddress "127.0.0.1"
#define _len 50

void startAsServer();
void startAsClient();

void main()
{
	char mode[2] = {};

	while(true)
	{
		cout << "Press S for server or C for client: ";
		cin >> mode;

		if(*mode == 's' || *mode == 'S')
			startAsServer();

		else if(*mode == 'c' || *mode == 'C')
			startAsClient();

		else
			cout << "Undefined command... Try again" << endl << endl;
	}
}

void startAsServer()
{
	cout << "Server says: Hi ;)" << endl;

	//Initialization
	INET_Addr addressLog = INET_Addr(_portLog, INADDR_ANY);
	INET_Addr addressAlarm = INET_Addr(_portAlarm, INADDR_ANY);
	INET_Addr addressPatient = INET_Addr(_portPatient, INADDR_ANY);

	LogEvent logEvent = LogEvent(addressLog, SERVER,"");
	AlarmEvent alarmEvent = AlarmEvent(addressAlarm, SERVER,0,"");
	PatientValueEvent patientEvent = PatientValueEvent(addressPatient, SERVER,0,"");

	ConcreteReactor conReactor = ConcreteReactor();
	RefinedReactor refinedReactor = RefinedReactor(&conReactor);

	refinedReactor.register_handler(&logEvent, ACCEPT_EVENT);
	refinedReactor.register_handler(&alarmEvent, ACCEPT_EVENT);
	refinedReactor.register_handler(&patientEvent, ACCEPT_EVENT);

	refinedReactor.register_handler(&logEvent, READ_EVENT);
	refinedReactor.register_handler(&alarmEvent, READ_EVENT);
	refinedReactor.register_handler(&patientEvent, READ_EVENT);

	cout << "Server says: Ready :D" << endl << endl;

	timeval time;
	time.tv_sec = 0;
	time.tv_usec = 500000;

	//Application
	while(true)
	{
		cout << ".";
		refinedReactor.handle_events(&time);
	}
}

void startAsClient()
{
	cout << "Client says: Hi ;)" << endl << endl;
	char ipaddress[50] = "";
	const char localHost[10] = _localAddress;
	char* selectedAddress;
	bool connected = false;
	ConcreteReactor conReactor = ConcreteReactor();
	RefinedReactor refinedReactor = RefinedReactor(&conReactor);

	while(!connected)
	{
		selectedAddress = ipaddress;
		cout << "Write IP address (type L to use local host): " << endl;
		cin >> selectedAddress;

		if(*selectedAddress == 'l' || *selectedAddress == 'L')
			selectedAddress = (char*)localHost;

		INET_Addr logAddress = INET_Addr(_portLog, selectedAddress);
		INET_Addr alarmAddress = INET_Addr(_portAlarm, selectedAddress);
		INET_Addr patientAddress = INET_Addr(_portPatient, selectedAddress);

		LogEvent logEvent = LogEvent(logAddress, CLIENT,"");;
		AlarmEvent alarmEvent = AlarmEvent(alarmAddress, CLIENT,0,"");
		PatientValueEvent patientEvent = PatientValueEvent(patientAddress, CLIENT,0,"");

		cout << "Connecting to: " << selectedAddress << endl;
	
		timeval time;
		time.tv_sec = 3;
		time.tv_usec = 0;

		refinedReactor.register_handler(&logEvent,INIT_EVENT);
		refinedReactor.register_handler(&alarmEvent,INIT_EVENT);
		refinedReactor.register_handler(&patientEvent,INIT_EVENT);

		connected = refinedReactor.handle_events(&time);

		if(connected)
		{
			cout << "Connected!" << endl << endl;
		}
		else
		{
			cout << "Could not connect. Resetting..." << endl << endl;
			refinedReactor.remove_handler(&logEvent, INIT_EVENT);
			refinedReactor.remove_handler(&alarmEvent, INIT_EVENT);
			refinedReactor.remove_handler(&patientEvent, INIT_EVENT);
		}
	}
	while(1)
	{
		bool validInput = false;
		char selectedEventType[1] = {};
		cout << endl << "Select event type:" << endl <<
			"   (1) Log event" << endl <<
			"   (2) Alarm event" << endl <<
			"   (3) Patient value event" << endl;

		cin >> selectedEventType;
		INET_Addr logAddress = INET_Addr(_portLog, selectedAddress);
		INET_Addr alarmAddress = INET_Addr(_portAlarm, selectedAddress);
		INET_Addr patientAddress = INET_Addr(_portPatient, selectedAddress);
		
		LogEvent *logEvent = NULL;
		AlarmEvent *alarmEvent = NULL;
		PatientValueEvent *patientEvent = NULL;

		if(selectedEventType[0] == '1')
		{
			const int len = 50;
			char logData[len] = {};

			cout << endl << "Enter log data:" << endl;
			cin >> logData;	

			logEvent = new LogEvent(logAddress,CLIENT,logData);
			refinedReactor.register_handler(logEvent, WRITE_EVENT);
			validInput = true;
		}

		else if(selectedEventType[0] == '2')
		{
			const int len = 50;
			char textString[len] = {};
			char priority[2] = {};

			cout << endl << "Enter alarm priority:" << endl;
			cin >> priority;
			cout << endl << "Enter text: " << endl;
			cin >> textString;			

			alarmEvent = new AlarmEvent(alarmAddress,CLIENT,atoi(priority),textString);
			refinedReactor.register_handler(alarmEvent, WRITE_EVENT);
			validInput = true;
		}

		else if(selectedEventType[0] == '3')
		{
			const int len = 50;
			char type[len] = {};
			char value[len] = {};
			cout << endl << "Enter patient type (number):" << endl;
			cin >> type;
		
			cout << endl << "Enter value: " << endl;
			cin >> value;
			patientEvent = new PatientValueEvent(patientAddress,CLIENT,atoi(type),value);
			refinedReactor.register_handler(patientEvent, WRITE_EVENT);
			validInput = true;
		}

		else
		{
			cout << selectedEventType << " is an undefined command... Try again" << endl;	
		}

		timeval time;
		time.tv_sec = 1;
		time.tv_usec = 0;
		if(validInput)
		{
			if(refinedReactor.handle_events(&time))
			{
				cout << endl << "Transmission successfull" << endl << endl;
			}
			else
			{
				cout << "Transmission failed!" << endl << endl;
			}
			
			if(logEvent != NULL)
				delete logEvent;
			else if (alarmEvent != NULL)
				delete alarmEvent;
			else if (patientEvent != NULL)
				delete patientEvent;
		}
	}	
}